package common.html;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang.StringUtils;

public abstract class Tag
{
    TreeMap<String, String> attrMap = new TreeMap<String, String>();
    String content = "";
    public final static String newLine = "\n";

    public Tag open(HtmlWriter hw)
    {
        hw.print(openTag());
        return this;
    }

    public Tag close(HtmlWriter hw)
    {
        String str = closeTag();
        if (StringUtils.isNotBlank(str))
        {
            hw.print(str);
        }
        return this;
    }

    public Tag openAndCloseWithEmptyContent(HtmlWriter hw)
    {
        hw.print(openAndCloseWithEmptyContentTag());
        return this;
    }

    public abstract String getTagName();

    public String openAndCloseWithEmptyContentTag()
    {
        String format = StringUtils.isNotBlank(attr()) ? "<%s %s/>" : "<%s/>";
        return String.format(format, getTagName(), attr());
    }

    public String openTag()
    {
        String format = StringUtils.isNotBlank(attr()) ? "<%s %s>" : "<%s>";
        return String.format(format, getTagName().toLowerCase(), attr());
    }

    public String closeTag()
    {
        return String.format("</%s>", getTagName()).toLowerCase();
    }

    public String getContentTag()
    {
        return content == null ? "" : content;
    }

    public String getContentWithOpenAndCloseTag()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(openTag());
        newLine(sb);
        sb.append(getContentTag());
        newLine(sb);
        sb.append(closeTag());
        newLine(sb);
        return sb.toString();
    }

    protected StringBuilder newLine(StringBuilder sb)
    {
        sb.append(newLine);
        return sb;
    }

    protected String newLine(String str)
    {
        return str == null ? newLine : str.concat(newLine);
    }

    public Tag content(String content)
    {
        this.content = content;
        return this;
    }

    public ArrayList<Tag> subTag = new ArrayList<Tag>();

    public Tag content(Tag... subTag)
    {
        StringBuilder sb = new StringBuilder();
        for (Tag tag : subTag)
        {
            sb.append(tag.getContentWithOpenAndCloseTag());
        }
        this.content = sb.toString();
        synchronized (this.subTag)
        {
            this.subTag.clear();
            if (subTag != null)
            {
                Collections.addAll(this.subTag, subTag);
            }
        }

        return this;
    }

    public Tag after(Tag... subTag)
    {
        StringBuilder sb = new StringBuilder();
        for (Tag tag : subTag)
        {
            sb.append(tag.getContentWithOpenAndCloseTag());
        }
        this.content = StringUtils.join(new Object[] { this.content, sb.toString() });

        synchronized (this.subTag)
        {
            if (subTag != null)
            {
                Collections.addAll(this.subTag, subTag);
            }
        }
        return this;
    }

    public Tag before(Tag... subTag)
    {
        StringBuilder sb = new StringBuilder();
        for (Tag tag : subTag)
        {
            sb.append(tag.getContentWithOpenAndCloseTag());
        }

        this.content = this.content == null ? "" : this.content.concat(sb.toString());

        synchronized (this.subTag)
        {
            ArrayList<Tag> newTags = new ArrayList<Tag>();
            if (subTag != null)
            {
                Collections.addAll(newTags, subTag);
                newTags.addAll(this.subTag);
            }
            this.subTag = newTags;
        }

        return this;
    }

    public Tag putAllAttr(Map<String, String> attr)
    {
        if (null == attr)
            return this;
        attrMap.putAll(attr);
        return this;
    }

    public Tag putAttr(String key, String value)
    {
        attrMap.put(key, value);
        return this;
    }

    public String attr()
    {
        if (attrMap.isEmpty())
            return "";
        StringBuilder sb = new StringBuilder();
        for (String key : attrMap.keySet())
        {
            String value = attrMap.get(key);
            if (StringUtils.isNotBlank(value))
            {
                sb.append(String.format(" %s=\"%s\"", key.toLowerCase(), attrMap.get(key)));
            }
        }
        return sb.toString();
    }

    public Tag putclass(String className)
    {
        Css css = new Css(className);
        css.add(this);
        return this;
    }

    public void write(HtmlWriter hw)
    {
        hw.print(getContentWithOpenAndCloseTag());
    }

    public static class BodyTag extends Tag
    {

        public String getTagName()
        {
            return "body";
        }

    }

    public static class HeadTag extends Tag
    {

        public String getTagName()
        {
            return "head";
        }

    }

    public abstract static class SpecialTag extends Tag
    {

        public String closeTag()
        {
            return "";
        }
    }

    public static class DOCTYPETag extends SpecialTag
    {

        public String getTagName()
        {
            return "!DOCTYPE";
        }

        public String attr()
        {
            return "html";
        }

    }

    public static class HtmlTag extends Tag
    {
        public HtmlTag()
        {
            super();
            putAttr("lang", "en");
        }

        public String getTagName()
        {
            return "html";
        }
    }

    public static class PageTag extends SpecialTag
    {
        HtmlTag html;
        BodyTag body;
        HeadTag head;
        static DOCTYPETag doc = new DOCTYPETag();

        public PageTag()
        {
            html = new HtmlTag();
            body = new BodyTag();
            head = new HeadTag();
        }

        public String openTag()
        {
            return "";
        }

        public String getContentWithOpenAndCloseTag()
        {
            delaySendContent();
            StringBuilder sb = new StringBuilder();
            sb.append(doc.getContentWithOpenAndCloseTag());
            sb.append(super.getContentWithOpenAndCloseTag());
            return sb.toString();
        }

        private void delaySendContent()
        {
            html.content(head, body);
            this.content(html);
        }

        public String getTagName()
        {
            return "";
        }

        public HtmlTag getHtml()
        {
            return html;
        }

        public BodyTag getBody()
        {
            return body;
        }

        public PageTag addBodayElement(Tag... tags)
        {
            this.body.after(tags);
            return this;
        }

        public PageTag addHeaderElement(Tag... tags)
        {
            this.head.after(tags);
            return this;
        }

        public HeadTag getHead()
        {
            return head;
        }

    }


    public static class Text extends SpecialTag
    {
        String format;
        String content;

        public Text(String format)
        {
            this.format = format;
            setContent(format);
        }

        public Text(String format, String... args)
        {
            this(format);
            setContent(String.format(format, args));
        }

        public String getFormat()
        {
            return format;
        }

        public void setFormat(String format)
        {
            this.format = format;
        }

        public String getContent()
        {
            return content;
        }

        public void setContent(String content)
        {
            this.content = content;
        }

        public void addArgs(String... args)
        {
            setContent(String.format(format, args));
        }

        @Override
        public String toString()
        {
            return getContent();
        }

        @Override
        public String getTagName()
        {
            return "text";
        }

        public String openTag()
        {
            return "";
        }

        public String getContentTag()
        {
            return getContent();
        }

    }

    public static void main(String[] args)
    {
        PageTag pt = new PageTag();

        DefaultTag fromTag =
                        TagFactory.createTag("form", new String[][] { { "action", "/" }, { "method", "POST" },
                                        { "enctype", "multipart/form-data" } });
        DefaultTag pTag = TagFactory.createTag("p");
        DefaultTag inputTag =
                        TagFactory.createTag("input",
                            new String[][] { { "type", "file" }, { "name", "fileUpload" } });
        DefaultTag buttonTag =
                        TagFactory.createTag("button ", new String[][] { { "type", "submit" } });
        buttonTag.content("Upload");
        pTag.content(inputTag, buttonTag);
        fromTag.content(pTag);

        pt.addBodayElement(fromTag);
        pt.addHeaderElement(new LinkTag("//netdna.bootstrapcdn.com/bootstrap/3.0.1/css/bootstrap.min.css"));
        // pt.write(new HtmlWriter(new PrintWriter(System.out)));
        System.out.println(pt.getContentWithOpenAndCloseTag());

    }
}
